%%
%% Share the load of the different configuration with an aim to shift a maximum CPU DeltaCPU
%% and a maximum Memory increase MAXMEM.
%% 
%% Parameters:
%% 	PLANLD: Different configuration plan parts for the node Large
%%  PLANSD: Different parts for the node Small
%%  DeltaCPU: CPU load to shift
%%  MAXMEM: Maximum memory to increase for the node Small.
%%  
%%  Parameter formats: PLAN = [{CID, CPU, MEM}]
%%
share_different(PLANLD, PLANSD, DeltaCPU, MAXMEM)->
	PLANs = prepare_plans(PLANLD, [{[], 0, 0}], 1, DeltaCPU, MAXMEM),
	{CIDs, CPU, MEM} = lists:nth(1, PLANs),
	DeltaCPUNew = DeltaCPU - CPU,
	MAXMEMNew = MAXMEM - MEM,
	ListL = [{CID, {CPU, MEM}} || {CID, CPU, MEM} <- PLANLD],
	DictL = dict:from_list(ListL),
	{DictLNew, PLANLDNew, PLANSDNew} = share_different_exec(DictL, PLANSD, CIDs, 1),
	if
		DeltaCPUNew == 0 ->
			{PLANLDNew, PLANSDNew, 0};
		true ->			
			PLANLDNewFiltered = [{CID, CPU, MEM} || {CID, CPU, MEM} <- PLANLDNew, CPU >= DeltaCPUNew, MEM =< MAXMEMNew],
			if
				length(PLANLDNewFiltered) >0 ->
					PLANLDNewFilteredSorted = sort_mem(PLANLDNewFiltered, 1),
					{CIDF, CPUF, MEMF} = lists:nth(1, PLANLDNewFilteredSorted),
					DictLNewNew = dict:store(CIDF, {CPUF - DeltaCPUNew, MEMF}, DictLNew),
					PLANSDNewNew = lists:append(PLANSDNew, [{CIDF, DeltaCPUNew, MEMF}]),
					PLANLDNewNew = dict_to_list(DictLNewNew),
					{PLANLDNewNew, PLANSDNewNew, 0};
				true->
					{PLANLDNew, PLANSDNew, DeltaCPUNew}
			end
	end.

%%
%% With calculated shifting CIDs to shift from node Large to node Small
%% 
%% Parameters:
%% 	DictL: Dictionary of the current still available different configuration items in node Large
%%  PLANSD: Different parts for the node Small
%%  CIDs: CIDs to execute the shifting
%%  Index: Current shifting CID with Index in CIDs
%%  
%%  Parameter formats: PLAN = [{CID, CPU, MEM}]
%%
share_different_exec(DictL, PLANSD, CIDs, Index)->
	if
		Index > length(CIDs)->
			PLANLD = dict_to_list(DictL),
			{DictL, PLANLD, PLANSD};
		true->
			CID = lists:nth(Index, CIDs),
			{CPU, MEM} = dict:fetch(CID, DictL),
			PLANSDNew = lists:append(PLANSD, [{CID, CPU, MEM}]),
			DictLNew = dict:erase(CID, DictL),
			share_different_exec(DictLNew, PLANSDNew, CIDs, Index+1)
	end.

%%
%% Prepare all possible combinations of items in PLANLD.
%% 
%% Parameters:
%% 	PLANLD: Different configuration plan parts for the node Large
%%  Current: Current calcuated combinations
%%  Index: Current item being added to the combinations
%%  DeltaCPU: CPU load to shift
%%  MAXMEM: Maximum memory to increase for the node Small.
%%  
%%  Parameter formats: PLAN = [{CID, CPU, MEM}]
%%
prepare_plans(PLANLD, Current, Index, DeltaCPU, MAXMEM)->
	if
		Index > length(PLANLD)->
			Current;
		true ->
			{CID, CPU, MEM} = lists:nth(Index, PLANLD),
			CurrentNewGenerated = [{lists:append(EIDs, [CID]), ECPU + CPU, EMEM + MEM} || {EIDs, ECPU, EMEM} <- Current, ECPU + CPU =< DeltaCPU, EMEM + MEM =< MAXMEM],
			CurrentNew = lists:merge(Current, CurrentNewGenerated),
			CurrentNewFiltered = filter(CurrentNew),
			prepare_plans(PLANLD, CurrentNewFiltered, Index+1, DeltaCPU, MAXMEM)
	end.

%%
%% Filter plan combinations to make sure, after sorted by CPU decreased, 
%% MEM is always decreased.
%%
%% Parameters:
%% 	PLAN: Configuration combinations to be filtered.
%%
filter(PLAN)->
	if
		length(PLAN) ==0 ->
			PLAN;
		true ->
			PLANSorted = sort_cpu(PLAN, -1),
			First = lists:nth(1, PLANSorted),
			filter(PLANSorted, [First], 2)
	end.

%%
%% Filter plan combinations to make sure, after sorted by CPU decreased, 
%% MEM is always decreased (Being calculated).
%%
%% Parameters:
%% 	PLAN: Configuration combinations to be filtered.
%%  PLANNew: Currently filtered results.
%%  Index: Combination being scanned.
%%
filter(PLAN, PLANNew, Index)->
	if
		Index > length(PLAN)->
			PLANNew;
		true ->
			Latest = lists:nth(length(PLANNew), PLANNew),
			{_CIDsLatest, _CPULatest, MEMLatest} = Latest,
			Current = lists:nth(Index, PLAN),
			{_CIDs, _CPU, MEM} = Current,
			if
				MEM < MEMLatest ->
					filter(PLAN, lists:append(PLANNew, [Current]), Index+1);
				true ->
					filter(PLAN, PLANNew, Index+1)
			end
	end.